﻿using Miao;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Serialization;

namespace Miao.ML
{
	public class Deanalyzer
	{
		private static readonly List<Assembly> assemblyList;
		//基础对象反序列化措施
		private static readonly Dictionary<Type, Func<WinMLData, object>> predefineDeserializationDictionary;

		private readonly Dictionary<string, Type> typeDictionary = new Dictionary<string, Type>(7);

		private readonly Dictionary<int, object> objectDictionary = new Dictionary<int, object>();

		private readonly Dictionary<string, Func<string, object>> customDeserializationFunctionDictionary = new Dictionary<string, Func<string, object>>();

		static Deanalyzer()
		{
			assemblyList = AppDomain.CurrentDomain.GetAssemblies().ToList();
			predefineDeserializationDictionary = new Dictionary<Type, Func<WinMLData, object>>(7);
			AppDomain.CurrentDomain.AssemblyLoad += delegate(object _, AssemblyLoadEventArgs assemblyLoadEventArgs)
			{
				assemblyList.Add(assemblyLoadEventArgs.LoadedAssembly);
			};
			predefineDeserializationDictionary.Add(TypeCache.intType, IntAnalyticFunction);
			predefineDeserializationDictionary.Add(TypeCache.longType, LongAnalyticFunction);
			predefineDeserializationDictionary.Add(TypeCache.boolType, BoolAnalyticFunction);
			predefineDeserializationDictionary.Add(TypeCache.floatType, FloatAnalyticFunction);
			predefineDeserializationDictionary.Add(TypeCache.doubleType, DoubleAnalyticFunction);
			predefineDeserializationDictionary.Add(TypeCache.stringType, StringAnalyticFunction);
		}

		public Deanalyzer()
		{
			typeDictionary.Add("System.Int32", TypeCache.intType);
			typeDictionary.Add("System.Int64", TypeCache.longType);
			typeDictionary.Add("System.Single", TypeCache.floatType);
			typeDictionary.Add("System.Boolean", TypeCache.boolType);
			typeDictionary.Add("System.Double", TypeCache.doubleType);
			typeDictionary.Add("System.String", TypeCache.stringType);
		}
		/// <summary>
		/// 从WinMLData数据转为所需对象数据
		/// </summary>
		/// <param name="winMLdata"></param>
		/// <param name="objectType"></param>
		/// <returns></returns>
		public object Deanalyze(WinMLData winMLdata, Type objectType)
		{
			try
			{
				return Analyze(winMLdata, objectType);
			}
			finally
			{
				objectDictionary.Clear();
			}
		}

		public void AddDeserializationFunction<T>(Func<string, T> deserializationFunction)
		{
			Func<string, T> deserializationFunction2 = deserializationFunction;
			string fullName = typeof(T).FullName;
			if (customDeserializationFunctionDictionary.TryAdd(fullName, (string text) => deserializationFunction2(text)))
			{
				return;
			}
			throw new Exception("WinMLDeserializer.AddDeserializationFunction: 解析类型 " + fullName + " 已有一个解析方法, 不能重复添加!");
		}

		private static object IntAnalyticFunction(WinMLData winMLData)
		{
			if (winMLData is IntData intData)
			{
				return intData.value;
			}
			throw new WinMLDataTypeException($"实际类型是 int, 但winML中记录的类型是 {winMLData.type}");
		}

		private static object LongAnalyticFunction(WinMLData winMLData)
		{
			if (winMLData is LongData longData)
			{
				return longData.value;
			}
			if (winMLData is IImplicitConversionToLong implicitConversionToLong)
			{
				return implicitConversionToLong.longValue;
			}
			throw new WinMLDataTypeException($"实际类型是 long, 但winML中记录的类型是 {winMLData.type}");
		}

		private static object BoolAnalyticFunction(WinMLData winMLData)
		{
			if (winMLData is BoolData boolData)
			{
				return boolData.value;
			}
			throw new WinMLDataTypeException($"实际类型是 bool, 但winML中记录的类型是 {winMLData.type}");
		}

		private static object FloatAnalyticFunction(WinMLData winMLData)
		{
			if (winMLData is FloatData floatData)
			{
				return floatData.value;
			}
			if (winMLData is IImplicitConversionToFloat implicitConversionToFloat)
			{
				return implicitConversionToFloat.floatValue;
			}
			throw new WinMLDataTypeException($"实际类型是 float, 但winML中记录的类型是 {winMLData.type}");
		}

		private static object DoubleAnalyticFunction(WinMLData winMLData)
		{
			if (winMLData is DoubleData doubleData)
			{
				return doubleData.value;
			}
			if (winMLData is IImplicitConversionToDouble implicitConversionToDouble)
			{
				return implicitConversionToDouble.doubleValue;
			}
			throw new WinMLDataTypeException($"实际类型是 double, 但winML中记录的类型是 {winMLData.type}");
		}

		private static object StringAnalyticFunction(WinMLData winMLData)
		{
			if (winMLData is StringData stringData)
			{
				return stringData.value;
			}
			throw new WinMLDataTypeException($"实际类型是 string, 但winML中记录的类型是 {winMLData.type}");
		}

		private static object CreateObject(Type objectType, out bool constructed)
		{
			constructed = objectType.GetConstructor(Type.EmptyTypes) != null;
			if (!constructed)
			{
				return FormatterServices.GetUninitializedObject(objectType);//只创建对象不调用构造函数
			}
			return Activator.CreateInstance(objectType);
		}

		private object Analyze(WinMLData winMLData, Type objectType)
		{
			if (winMLData is INullable nullable && nullable.isNull)
			{
				return null;
			}
			if (objectType == TypeCache.objectType)//最基础对象判断
			{
				return PostProcess(ObjectAnalyticFunction(winMLData));
			}
			if (predefineDeserializationDictionary.TryGetValue(objectType, out var value))//基础字段和自定义对象的反序列化措施
			{
				return PostProcess(value(winMLData));
			}
			switch (winMLData.type)
			{
			case DataType.Enum:
				return PostProcess(CreateEnum(winMLData));
			case DataType.Custom:
				return PostProcess(CustomAnalyticFunction(winMLData as CustomData));
			case DataType.Nullable:
				return PostProcess(NullableAnalyticFunction(winMLData as NullableData));
			case DataType.Reference:
				return PostProcess(ReferenceAnalyticFunction(winMLData as ReferenceData));
			case DataType.Method:
				return MethodAnalyticFunction(winMLData as GenericMulticastMethodData);
                //return MethodAnalyticFunction(winMLData as GenericMulticastMethodData, objectType);
			default:
				if (objectType.IsArray)//这个得有空优化，我可艹了winML还是不支持数组。
				{
					throw new Exception("WinMLDeserializer.Analyze: 不支持数组, 请改用List<T>");
				}
				if (objectType.IsGenericType)
				{
					if (objectType.GetInterface("System.Collections.IList") != null)
					{
						return PostProcess(CreateList(winMLData as ListData, objectType));
					}
					if (objectType.GetInterface("System.Collections.IDictionary") != null)
					{
						return PostProcess(CreateDictionary(winMLData as DictionaryData, objectType));
					}
					if (winMLData.type != 0)
					{
						return null;
					}
					return PostProcess(AnalyzeGenericObject(winMLData as ObjectData, objectType));
				}
				if (winMLData.type != 0)
				{
					return null;
				}
				//序列化正常对象
				return PostProcess(AnalyzeObject(winMLData as ObjectData, objectType));
			}
			static object PostProcess(object result)
			{
				if (result is IDeserializationEvent deserializationEvent)
				{
					deserializationEvent.OnDeserialized();
				}
				return result;
			}
		}
        //开始将数据化对象序列化为实际要的对象
        //采用qualifiedName而非type递归的方式是为了采用报错
        private object AnalyzeObject(ObjectData objectData, Type objectType)
		{
			string qualifiedName = objectData.qualifiedName;
			if (objectData.isGenericType)
			{
				objectType = MakeGenericType(objectData, objectType);
			}
			else if (objectType.FullName != qualifiedName)
			{
				Type type = TryFindMatchType(qualifiedName, objectType);
				if (!objectType.IsAssignableFrom(type))
				{
					throw new Exception("WinMLDeserializer.AnalyzeObject: winML序列化的类型为 " + qualifiedName + ", 但实际类型为 " + objectType.FullName);
				}
				objectType = type;
			}
			bool constructed;
			object obj = CreateObject(objectType, out constructed);
			AnalyzeDataIntoInstance(objectData, obj, objectType, constructed);
			return obj;
		}

		private object AnalyzeGenericObject(ObjectData objectData, Type closeType)
		{
			bool constructed;
			object obj = CreateObject(closeType, out constructed);
			AnalyzeDataIntoInstance(objectData, obj, closeType, constructed);
			return obj;
		}

		private Type FindType(string typeQualifiedName)
		{
			if (!typeDictionary.TryGetValue(typeQualifiedName, out var value))
			{
				return GetTypeFromAppDomain(typeQualifiedName);
			}
			return value;
		}


		private Type GetTypeFromAppDomain(string typeQualifiedName)
		{
			foreach (Assembly assembly in assemblyList)
			{
				Type type = assembly.GetType(typeQualifiedName);
				if (type != null)
				{
					typeDictionary.Add(typeQualifiedName, type);
					return type;
				}
			}
			throw new Exception("WinMLDeserializer.GetTypeFromAppDomain: 无法在当前应用程序域下找到类型 " + typeQualifiedName);
		}

        private Type TryFindMatchType(string qualifiedName, Type parentType)
        {
            if (typeDictionary.TryGetValue(qualifiedName, out Type value))
            {
                return value;
            }
            value = parentType.Assembly.GetType(qualifiedName);
            if (value != null)
            {
                typeDictionary.Add(qualifiedName, value);
                return value;
            }
            return GetTypeFromAppDomain(qualifiedName);
        }

        private Type MakeGenericType(IGeneric generic, Type parentType)
		{
			if (!generic.isGenericType)
			{
				return TryFindMatchType(generic.qualifiedName, parentType);
			}
			Type type = TryFindMatchType(generic.openTypeName, parentType);
			if (generic.genericArgumentCount == 1)
			{
				return type.MakeGenericType(MakeGenericType(generic.GetGenericArgument(0), type));
			}
			Type[] array = new Type[generic.genericArgumentCount];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = MakeGenericType(generic.GetGenericArgument(i), parentType);
			}
			return type.MakeGenericType(array);
		}

		private Type MakeGenericType(Type openType, IGeneric generic)
		{
			if (generic.isGenericType)
			{
				Type[] array = new Type[generic.genericArgumentCount];
				int num = 0;
				foreach (IGeneric genericArgument in generic.genericArguments)
				{
					array[num] = MakeGenericType(FindType(genericArgument.qualifiedName), genericArgument);
					num++;
				}
				return openType.MakeGenericType(array);
			}
			return openType;
		}

		private void FillDataIntoList(ListData listData, IList list, Type listType)
		{
			if (listData.isNone)
			{
				return;
			}
			Type objectType = listType.GenericTypeArguments[0];
			foreach (WinMLData listDatum in listData)
			{
				list.Add(Analyze(listDatum, objectType));
			}
		}

		private void FillDataIntoDictionary(DictionaryData dictionaryData, IDictionary dictionary, Type dictionaryType)
		{
			if (dictionaryData.isNone)
			{
				return;
			}
			Type[] genericArguments = dictionaryType.GetGenericArguments();
			foreach (KeyValuePair<WinMLData, WinMLData> item in dictionaryData.dataDictionary)
			{
				dictionary.Add(Analyze(item.Key, genericArguments[0]), Analyze(item.Value, genericArguments[1]));
			}
		}

		private void AnalyzeDataIntoInstance(ObjectData objectData, object obj, Type objType, bool constructed)
		{
			if (!objType.IsValueType)
			{
				objectDictionary.Add(objectData.referenceID, obj);
			}
			if (objectData.isNone)
			{
				return;
			}
			if (constructed)
			{
				foreach (KeyValuePair<string, WinMLData> objectDatum in objectData)
				{
					WinMLData winData = objectDatum.Value;//解析下一层数据
					FieldInfo field = objType.GetField(objectDatum.Key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						//只收对象和链表结构
						if (winData.type == DataType.List || winData.type == DataType.Dictionary || (winData.type == DataType.Object && (winData as ReferenceObjectData).referenceID != -1))
						{
							if ((winData as INullable).isNull)
							{
								field.SetValue(obj, null);
							}
							else
							{
								object value2 = field.GetValue(obj);//返回位于rootData下的自身实例
								if (value2 != null)
								{
									FastFillReferenceObject(winData, value2);
								}
								else
								{
									field.SetValue(obj, Analyze(winData, field.FieldType));
								}
							}
						}
						else
						{
							//设置字段
							field.SetValue(obj, Analyze(winData, field.FieldType));
						}
					}
					else
					{
						PropertyInfo property = objType.GetProperty(objectDatum.Key, BindingFlags.Instance | BindingFlags.Public);
						if (property != null)
						{
							if (winData.type == DataType.List || winData.type == DataType.Dictionary || (winData.type == DataType.Object && (winData as ReferenceObjectData).referenceID != -1))
							{
								if ((winData as INullable).isNull)
								{
									property.SetValue(obj, null);
								}
								else
								{
									object value3 = property.GetValue(obj);
									if (value3 != null)
									{
										FastFillReferenceObject(winData, value3);
									}
									else
									{
										property.SetValue(obj, Analyze(winData, property.PropertyType));
									}
								}
							}
							else
							{
								property.SetValue(obj, Analyze(winData, property.PropertyType));
							}
						}
					}
				}
				return;
			}
			foreach (KeyValuePair<string, WinMLData> objectDatum2 in objectData)
			{
				FieldInfo field2 = objType.GetField(objectDatum2.Key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field2 != null)
				{
					field2.SetValue(obj, Analyze(objectDatum2.Value, field2.FieldType));
					continue;
				}
				PropertyInfo property2 = objType.GetProperty(objectDatum2.Key, BindingFlags.Instance | BindingFlags.Public);
				property2?.SetValue(obj, Analyze(objectDatum2.Value, property2.PropertyType));
			}
			//快速填充引用对象
			void FastFillReferenceObject(WinMLData winMLData, object obj)
			{
				switch (winMLData.type)
				{
				case DataType.List:
				{
					IList list = obj as IList;
					list.Clear();
					FillDataIntoList(winMLData as ListData, list, obj.GetType());
					break;
				}
				case DataType.Object:
					AnalyzeDataIntoInstance(winMLData as ObjectData, obj, obj.GetType(), constructed: true);
					break;
				case DataType.Dictionary:
				{
					IDictionary dictionary = obj as IDictionary;
					dictionary.Clear();
					FillDataIntoDictionary(winMLData as DictionaryData, dictionary, obj.GetType());
					break;
				}
				default:
					throw new Exception($"意料之外的类型: {winMLData.type}");
				}
				if (obj is IDeserializationEvent deserializationEvent)
				{
					deserializationEvent.OnDeserialized();
				}
			}
		}

		private object CreateEnum(WinMLData winMLData)
		{
			if (!(winMLData is EnumData enumData))
			{
				throw new WinMLDataTypeException($"实际类型是 Enum, 但winML中记录的类型是 {winMLData.type}");
			}
			if (!Enum.TryParse(FindType(enumData.qualifiedName), enumData.value, out var result))
			{
				throw new Exception("WinMLDeserializer.CreateEnum: 无法从枚举 " + enumData.qualifiedName + " 中找到值 " + enumData.value);
			}
			return result;
		}

		private object CreateList(ListData listData, Type listType)
		{
			IList list = (IList)Activator.CreateInstance(listType);
			objectDictionary.Add(listData.referenceID, list);
			FillDataIntoList(listData, list, listType);
			return list;
		}

		private object CreateDictionary(DictionaryData dictionaryData, Type dictionaryType)
		{
			IDictionary dictionary = (IDictionary)Activator.CreateInstance(dictionaryType);
			objectDictionary.Add(dictionaryData.referenceID, dictionary);
			FillDataIntoDictionary(dictionaryData, dictionary, dictionaryType);
			return dictionary;
		}

		private object ObjectAnalyticFunction(WinMLData winMLData)
		{
			switch (winMLData.type)
			{
			case DataType.Object:
			{
				ObjectData objectData = winMLData as ObjectData;
				return AnalyzeObject(objectData, TypeCache.objectType);
			}
			case DataType.List:
			{
				ListData listData = winMLData as ListData;
				return CreateList(listData, MakeGenericType(listData, TypeCache.listOpenType));
			}
			case DataType.Dictionary:
			{
				DictionaryData dictionaryData = winMLData as DictionaryData;
				return CreateDictionary(dictionaryData, MakeGenericType(dictionaryData, TypeCache.dictionaryOpenType));
			}
			case DataType.Enum:
				return CreateEnum(winMLData);
			case DataType.Int:
				return IntAnalyticFunction(winMLData);
			case DataType.Long:
				return LongAnalyticFunction(winMLData);
			case DataType.Bool:
				return BoolAnalyticFunction(winMLData);
			case DataType.Float:
				return FloatAnalyticFunction(winMLData);
			case DataType.Double:
				return DoubleAnalyticFunction(winMLData);
			case DataType.String:
				return StringAnalyticFunction(winMLData);
			case DataType.Custom:
				return CustomAnalyticFunction(winMLData as CustomData);
			case DataType.Nullable:
				return NullableAnalyticFunction(winMLData as NullableData);
			case DataType.Reference:
				return ReferenceAnalyticFunction(winMLData as ReferenceData);
			default:
				throw new Exception("不可达分支异常");
			}
		}

		private object CustomAnalyticFunction(CustomData customData)
		{
			if (customDeserializationFunctionDictionary.TryGetValue(customData.qualifiedName, out var value))
			{
				object obj = value(customData.dataText);
				if (customData.referenceID != -1)
				{
					objectDictionary.Add(customData.referenceID, obj);
				}
				return obj;
			}
			throw new Exception("WinMLDeserializer.CustomAnalyticFunction: 类型 " + customData.qualifiedName + " 是一个自定义类型, 但没有对应的反序列化方法");
		}

		private object NullableAnalyticFunction(NullableData nullableData)
		{
			return Analyze(nullableData.value, TypeCache.objectType);
		}

		private object ReferenceAnalyticFunction(ReferenceData referenceData)
		{
			objectDictionary.TryGetValue(referenceData.referenceID, out var value);
			return value;
		}


        /// <summary>
        /// 这段可以取消传入objectType采用纯数据解析
        /// </summary>
        /// <param name="multicastMethodData"></param>
        /// <param name="objectType"></param>
        /// <returns></returns>
        private object MethodAnalyticFunction(GenericMulticastMethodData multicastMethodData)
		{
            Delegate multicasstAction = null;//创建一个多的Action多播类型
            if (multicastMethodData.isGenericType)
			{
				Type genericType = MakeGenericType(multicastMethodData, TypeCache.multicastDelegateType);
				//Type temp = MakeGenericType(FindType(multicastMethodData.qualifiedName), multicastMethodData);
				foreach (var method in multicastMethodData)
				{
					object target = ObjectAnalyticFunction(method.targetML);
					Type type = target.GetType();
					if (method.isStatic)
					{

					}
					else
					{
						MethodInfo methodInfo = type.GetMethod(method.MethodName, BindingFlags.Instance | BindingFlags.Public);
						MulticastDelegate action = Delegate.CreateDelegate(genericType, target, methodInfo) as MulticastDelegate;
						multicasstAction = Delegate.Combine(multicasstAction, action);
					}
				}
			}
			else
			{
                Type objectType = FindType(multicastMethodData.qualifiedName);
                foreach (var method in multicastMethodData)
				{
					object target = ObjectAnalyticFunction(method.targetML);
					Type type = target.GetType();
                    if (method.isStatic)
					{

					}
					else
					{
						MethodInfo methodInfo = type.GetMethod(method.MethodName, BindingFlags.Instance | BindingFlags.Public);
						MulticastDelegate action = Delegate.CreateDelegate(objectType, target, methodInfo) as MulticastDelegate;
						multicasstAction = Delegate.Combine(multicasstAction, action);
					}
				}
			}
			return multicasstAction;

		}
        /// <summary>
        /// 采用传入的Type来进行解析
        /// </summary>
        /// <param name="multicastMethodData"></param>
        /// <param name="objectType"></param>
        /// <returns></returns>
        private object MethodAnalyticFunction(GenericMulticastMethodData multicastMethodData, Type objectType)
		{
            Delegate multicasstAction = null;//创建一个多的Action多播类型

            foreach (var method in multicastMethodData)
            {
                object target = ObjectAnalyticFunction(method.targetML);
                Type type = target.GetType();
                if (method.isStatic)
                {

                }
                else
                {
                    MethodInfo methodInfo = type.GetMethod(method.MethodName, BindingFlags.Instance | BindingFlags.Public);
                    MulticastDelegate action = Delegate.CreateDelegate(objectType, target, methodInfo) as MulticastDelegate;
                    multicasstAction = Delegate.Combine(multicasstAction, action);
                }
            }
            return multicasstAction;

//泛型传入可以直接被找出，不需要实例化
   

            //创建静态或动态实例方法
            //Action d = Delegate.CreateDelegate(typeof(Action), target, methodInfo) as Action;
            //return d;
        }
    }
}
